package com.gl.organigrama.services.impl;

import java.util.ArrayList;
import java.util.List;

import org.hibernate.Hibernate;
import org.hibernate.collection.PersistentCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.flex.remoting.RemotingDestination;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.gl.organigrama.repository.IFuncionarioRepository;
import com.gl.organigrama.repository.INodoRepository;
import com.gl.organigrama.services.INodoServices;
import com.gl.organigrama.vos.Funcionario;
import com.gl.organigrama.vos.Nodo;

import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;


@RemotingDestination(channels = { "my-amf" })
@Service
@Transactional
public class NodoServices implements INodoServices{

	@Autowired
	private INodoRepository nodoRepository;
	@Autowired
	private IFuncionarioRepository funcionarioRepository;
	
	@Override
	public Nodo salvarNodo(Nodo nodo){
		if(nodo.getFuncionario() != null ){
			Funcionario funcionario = funcionarioRepository.save(nodo.getFuncionario());
			nodo.setFuncionario(funcionario);
		}
		nodoRepository.save(nodo);
		return nodo;
	}

	/* Servicio para recuperar el arbol que tenga un nodo al hacer click */
	@Override
	public List<Nodo> devolverArbol(Long idNodo){
		List<Nodo> hijosNivelDos = nodoRepository.recuperarHijosNivelDos(idNodo);
		return hijosNivelDos;
	}
	
	public Nodo get(Long id){
		return nodoRepository.find(id);
	}

	/* Servicio para recuperar el organigrama desde el Padre de todos */
	@Override
	public Nodo armarOrganigrama() throws Exception {
		Nodo nodoPadre = nodoRepository.getNodoPadre();
		return nodoPadre;
	}

	@Override
	public Nodo agregarHijoNivelUno(Long idNodoPadre, Long idNodoHijo) throws Exception {
		Nodo nodoPadre = nodoRepository.find(idNodoPadre);
		Nodo nodoHijo = nodoRepository.find(idNodoHijo);
		nodoRepository.recuperarHijosNivelUno(idNodoPadre);
		/* Si es su primer: Se lo setea, Si ya tiene hijos: Se los a�ade 
		 * Se Plateo asi, para no ir a buscar los hijos y luego a�adirselo
		 * */
		if( nodoPadre.getHijosNivelUno() != null && nodoPadre.getHijosNivelUno().size() > 0) {
			nodoPadre.getHijosNivelUno().add(nodoHijo);
		} else {
			List<Nodo> nodos = new ArrayList<Nodo>();
			nodos.add(nodoHijo);
			nodoPadre.setHijosNivelUno(nodos);
		}
		return nodoRepository.save(nodoPadre);
	}

	@Override
	public Nodo agregarHijoNivelDos(Long idNodoPadre, Long idNodoHijo) throws Exception {
		Nodo nodoPadre = nodoRepository.find(idNodoPadre);
		Nodo nodoHijo = nodoRepository.find(idNodoHijo);
		nodoRepository.recuperarHijosNivelDos(idNodoPadre);
		/* Si es su primer: Se lo setea, Si ya tiene hijos: Se los a�ade 
		 * Se Plateo asi, para no ir a buscar los hijos y luego a�adirselo
		 * */
		if( nodoPadre.getHijosNivelDos() != null && nodoPadre.getHijosNivelDos().size() > 0) {
			nodoPadre.getHijosNivelDos().add(nodoHijo);
		} else {
			List<Nodo> nodos = new ArrayList<Nodo>();
			nodos.add(nodoHijo);
			nodoPadre.setHijosNivelDos(nodos);
		}
		return nodoRepository.save(nodoPadre);
	}

	/* Lista los nodos, pero sin tener en cuenta el arbol */
	@Override
	public List<Nodo> listarNodos() throws Exception {
		
		List<Nodo> all = nodoRepository.findNodos();
		
		inflarColecciones(all);
		return all;
	}

	private void inflarColecciones(List<Nodo> all) {
		for(Nodo nodo:all){
			inflar(nodo.getHijosNivelUno());
			inflar(nodo.getHijosNivelDos());
		}
		
	}
	
	private void inflar(List collection){
		if(collection instanceof PersistentCollection)
		Hibernate.initialize(collection);
	}

	@Override
	public Nodo cambiarDePadreNivelUno(Long idNodoPadreNuevo,Long idNodoPadreAntes, Long idNodo) throws Exception {
		Nodo nodoPadreNuevo = nodoRepository.find(idNodoPadreNuevo);
		Nodo nodoPadreAntes = nodoRepository.find(idNodoPadreAntes);
		Nodo nodo = nodoRepository.find(idNodo);
		
		nodoPadreAntes.getHijosNivelUno().remove(nodo);
		nodoPadreAntes.setHijosNivelUno(nodoPadreAntes.getHijosNivelUno());
		
		if(nodoPadreNuevo.getHijosNivelUno() != null){
			nodoPadreNuevo.getHijosNivelUno().add(nodo);
			nodoPadreNuevo.setHijosNivelUno(nodoPadreNuevo.getHijosNivelUno());
		} else {
			List<Nodo> nodos = new ArrayList<Nodo>();
			nodos.add(nodo);
			nodoPadreNuevo.setHijosNivelUno(nodos);
		}

		nodoRepository.save(nodoPadreNuevo);
		nodoRepository.save(nodoPadreAntes);
		return nodoPadreNuevo;
	}
	
	
	@Override
	public Nodo cambiarDePadreNivelDos(Long idNodoPadreNuevo,Long idNodoPadreAntes, Long idNodo) throws Exception {
		Nodo nodoPadreNuevo = nodoRepository.find(idNodoPadreNuevo);
		Nodo nodoPadreAntes = nodoRepository.find(idNodoPadreAntes);
		Nodo nodo = nodoRepository.find(idNodo);
		
		nodoPadreAntes.getHijosNivelDos().remove(nodo);
		nodoPadreAntes.setHijosNivelDos(nodoPadreAntes.getHijosNivelDos());
		
		if(nodoPadreNuevo.getHijosNivelDos() != null){
			nodoPadreNuevo.getHijosNivelDos().add(nodo);
			nodoPadreNuevo.setHijosNivelDos(nodoPadreNuevo.getHijosNivelDos());
		} else {
			List<Nodo> nodos = new ArrayList<Nodo>();
			nodos.add(nodo);
			nodoPadreNuevo.setHijosNivelDos(nodos);
		}

		nodoRepository.save(nodoPadreNuevo);
		nodoRepository.save(nodoPadreAntes);
		return nodoPadreNuevo;
	}
	
	@Override
	public Nodo eliminarNodo (Nodo nodo, Nodo nodoPadre){		
		nodoRepository.save(nodoPadre);
		removeNodo(nodo);
		return nodoPadre;
	}
	
	@Override
	public Nodo getNodoPadre(Nodo nodo){
		return nodoRepository.getNodoPadre(nodo);
	}

	private void removeNodo(Nodo nodo){
		nodoRepository.remove(nodo);
	}
}
